#pragma once

#include <utility>
#include <sstream>

#include "HttpBase.hpp"

namespace network {

    class HttpRequest : public HttpBase {

    public:

        /**
          * @brief  构造一个空的Http请求
          * @note   该Http请求的方法默认为GET，版本默认为1.1，请求url为/
          */
        HttpRequest() : method_("GET"), url_("/") {}

        /**
          * @brief  按照请求方法和请求url构造一个空的Http请求
          * @param  method 请求方法
          * @param  url    请求url
          */
        HttpRequest(std::string method, std::string url) : method_(std::move(method)), url_(std::move(url)) {}

        /**
          * @brief  从字符串中解析Http请求
          * @note   如果解析原文不是Http格式会抛出std::runtime_error
          * @param  要解析的Http请求原始字符串
          * @retval 一个解析完成的Http请求对象
          */
        static HttpRequest parseRequest(const std::string &request);

        /**
          * @brief  将字符串解析为Http请求对象
          * @note   如果解析原文不是Http格式会抛出std::runtime_error
          * @param  request 要解析的Http请求原始字符串
          */
        void parse(const std::string &request);

        /**
          * @brief  设置请求URL
          * @param  要设置的URL字符串
          */
        void setUrl(const std::string &url) { url_ = url; }

        /**
          * @brief  获取请求URL
          * @retval url字符串
          */
        std::string getUrl() const;

        /**
          * @brief  设置请求方法
          * @param  要设置的请求方法
          */
        void setMethod(const std::string &method) { method_ = method; }

        /**
          * @brief  获取请求方法
          * @retval 请求方法
          */
        std::string getMethod() const { return method_; }

        /**
          * @brief  解析请求url中携带的参数
          * @note   暂不支持解析数组和对象结构参数
          * @retval 一个unordered_map<string,string>，key表示url请求参数中的键，value表示url请求参数中的值
          */
        std::unordered_map<std::string, std::string> getUrlParameter() const;

        /**
          * @brief  将Http请求类转化为字符串
          * @retval 一个字符串
          */
        std::string toString() const override;

    private:

        // 获取请求行
        void parseRequestLine(const std::string &line);

        // 获取一行请求头
        void parseHeaderLine(const std::string &header);

    private:

        // 请求方法
        std::string method_;

        // 请求URL
        std::string url_;

    };

    HttpRequest HttpRequest::parseRequest(const std::string &request) {
        HttpRequest req;
        req.parse(request);
        return req;
    }

    void HttpRequest::parse(const std::string &request) {
        std::istringstream stream(request);
        std::string line;
        if (!getline(stream, line)) {
            throw std::runtime_error("Empty request line");
        }
        parseRequestLine(line);
        while (getline(stream, line) && line != "\r" && !line.empty()) {
            parseHeaderLine(line);
        }
        std::streampos body_start_pos = stream.tellg();
        size_t remaining_length = request.size() - body_start_pos;
        if (remaining_length > 0) {
            body_ = request.substr(body_start_pos, remaining_length);
        } else {
            body_ = "";
        }
    }

    std::string HttpRequest::getUrl() const {
        std::string::size_type pos = url_.find('?');
        if (pos != std::string::npos) {
            return {&url_[0], &url_[pos]};
        }
        return url_;
    }

    std::unordered_map<std::string, std::string> HttpRequest::getUrlParameter() const {
        size_t start = url_.find_first_of('?');
        if (start == std::string::npos) {
            return std::unordered_map<std::string, std::string>();
        }
        size_t end = 0;
        std::unordered_map<std::string, std::string> ret;
        while (true) {
            end = url_.find_first_of('=', start + 1);
            if (end == std::string::npos) { break; }
            std::string key = std::string(&url_[start + 1], &url_[end]);
            start = end;
            end = url_.find_first_of('&', start + 1);
            if (end == std::string::npos) {
                std::string val = std::string(&url_[start + 1], &url_[url_.size()]);
                ret.emplace(key, val);
                break;
            }
            std::string val = std::string(&url_[start + 1], &url_[end]);
            start = end;
            ret.emplace(key, val);
        }
        return ret;
    }

    void HttpRequest::parseRequestLine(const std::string &line) {
        std::istringstream stream(line);
        if (!(stream >> method_ >> url_ >> version_)) {
            throw std::runtime_error("Failed to parse request line: " + line);
        }
    }

    void HttpRequest::parseHeaderLine(const std::string &header) {
        size_t colon_pos = header.find(':');
        if (colon_pos == std::string::npos) {
            throw std::runtime_error("Invalid header line: " + header);
        }
        std::string key = header.substr(0, colon_pos);
        if (header.at(colon_pos + 1) == ' ') {
            ++colon_pos;
        }
        std::string value = header.substr(colon_pos + 1);
        if (value[value.size() - 1] == '\r') {
            value.pop_back();
        }
        header_.emplace(key, value);
    }

    std::string HttpRequest::toString() const {
        std::ostringstream stream;
        stream << method_ << ' ' << url_ << ' ' << version_ << "\r\n";
        for (const auto &header: header_) {
            stream << header.first << ": " << header.second << "\r\n";
        }
        stream << "\r\n" << body_;
        return stream.str();
    }

}